unit uConverterThread;

interface

uses
  Windows, Classes;

type
  TConverterParam = record
    ThreadIdx: Word;
    GetTaskCycleSec: Integer;
    FinishTaskCycleSec: Integer;
    BoDelFinishFile: Boolean;
    TaskCount: Integer;
    BoShowProcess: Boolean;
    ConverterParam: string[255];
    ConverterParam_Pic: string[255];
    SrcFilePath: string[255];
    PreFixPath: string[255];
  end;
  PConverterParam = ^TConverterParam;

  TConverterTask = record
    ID: Integer;
    Path: string[255];
    Name: string[255];
    IsConverted: Integer;
    CName: string[255];
    ConvertName: string[255];
    Pic: string[255];
    Time: string[255];
    Px: string[255];
  end;
  PConverterTask = ^TConverterTask;
  
  PTaskNode = ^TTaskNode;
  TTaskNode = record
    Data: TConverterTask;
    Next: PTaskNode;
  end;
  
  TConverterThread = class(TThread)
  private
    FConvParam: TConverterParam;
    FTaskCs: TRTLCriticalSection;
    FTopNode: PTaskNode;
    FBotNode: PTaskNode;
    FExitEvent: THandle;
    FStopEvent: THandle;
    FTaskCount: Integer;
  protected
    procedure Execute; override;
  public
    constructor Create(convParam: TConverterParam);
    destructor Destroy; override;
    procedure AddTask(const task: TConverterTask);
    procedure Start;
    procedure Stop;
    property TaskCount: Integer read FTaskCount;
  end;
  
implementation

uses
  uGlobal, SysUtils;
  
constructor TConverterThread.Create(convParam: TConverterParam);
begin
  inherited Create(True);
  FConvParam := convParam;
  FExitEvent := CreateEvent(nil, True, False, nil);
  FStopEvent := CreateEvent(nil, True, False, nil);
  InitializeCriticalSection(FTaskCs);
end;

destructor TConverterThread.Destroy;
begin
  if Suspended then
    Resume;
  if not Terminated then
  begin
    Terminate;
    SetEvent(FExitEvent);
    WaitFor;
  end;
  DeleteCriticalSection(FTaskCs);
  CloseHandle(FStopEvent);
  CloseHandle(FExitEvent);
  inherited;
end;

procedure TConverterThread.Execute;
var
  workNode: PTaskNode;
  startupInfo: TStartupInfo;
  processInfo: TProcessInformation;
  handles: array[0..2] of THandle;
  nIndex, dwRet: DWORD;
  cmdLine: string;
begin
  workNode := nil;
  while not Terminated do
  begin
    EnterCriticalSection(FTaskCs);
    try
      if Assigned(FTopNode) then
      begin
        workNode := FTopNode;
        FTopNode := FTopNode.Next;
        if not Assigned(FTopNode) then
          FBotNode := nil;
      end else
        workNode := nil;
    finally
      LeaveCriticalSection(FTaskCs);
    end;
    
    if Assigned(workNode) then
    begin
      FillChar(startupInfo, Sizeof(TStartupInfo), 0);
      FillChar(processInfo, Sizeof(TProcessInformation), 0);
      FillChar(handles[0], Sizeof(handles), 0);

      startupInfo.cb := SizeOf(TStartupInfo);
      if FConvParam.BoShowProcess then
      begin
        startupInfo.wShowWindow := SW_SHOW;    
      end else
      begin
        startupInfo.dwFlags := STARTF_USESHOWWINDOW;
        startupInfo.wShowWindow := SW_HIDE;      
      end;
      cmdLine := ' -i ' + FConvParam.PreFixPath + workNode.Data.Path + workNode.Data.Name + ' ' +
                 FConvParam.ConverterParam + ' ' + workNode.Data.Name;
      if CreateProcess('ffmpeg.exe', //ApplicationName
                       PChar(cmdLine), //commandline
                       nil, //processAttributes
                       nil, //threadAttributes
                       False, //boInheritHandles
                       NORMAL_PRIORITY_CLASS, //CreationFlags
                       nil, //pEnvironment
                       nil, //pCurrentDirectory
                       startupInfo, //startupInfo
                       processInfo, //processInfo
                      ) then
      begin
        handles[0] := FExitEvent;
        handles[1] := FStopEvent;
        handles[2] := processInfo.hProcess;
        try
          dwRet := WaitForMultipleObjects(3, @handles[0], False, INFINITE);
          case dwRet of
            WAIT_TIMEOUT:
              begin
              end;
            WAIT_FAILED:
              begin
              end;
          else
          begin
            nIndex := dwRet - WAIT_OBJECT_0;
            case nIndex of
              0:
                Exit;  //exit thread
              1:
                begin
                  ResetEvent(FStopEvent);
                  continue;  //stop task
                end;
              2:
                begin //finish task
                  SendIMMsg(IM_TASK_COMPLEATE, 0, 0, 0, @workNode.data, sizeof(TConverterTask));
                end;
            end;
          end;
          end;
        finally
          TerminateProcess(processInfo.hProcess, 0);
          CloseHandle(processInfo.hProcess);
        end;
      end else
      begin
        DebugOut(Format('TaskID:%d CreateProcess lastError:%d error:%s',
          [FConvParam.ThreadIdx+1, GetLastError, SysErrorMessage(GetLastError)]));
      end;
      InterlockedDecrement(FTaskCount);
      Dispose(workNode);
    end;
    WaitForSingleObject(FExitEvent, 100);
  end;
end;

procedure TConverterThread.AddTask(const task: TConverterTask);
var
  p: PTaskNode;
begin
  New(p);
  FillChar(p^, Sizeof(TTaskNode), 0);
  p^.Data := task;
  EnterCriticalSection(FTaskCs);
  try
    if Assigned(FBotNode) then
      FBotNode.Next := p
    else
      FTopNode := p;
    FBotNode := p;
  finally
    LeaveCriticalSection(FTaskCs);
  end;
  InterlockedIncrement(FTaskCount);
end;

procedure TConverterThread.Stop;
begin
  SetEvent(FStopEvent);
end;

procedure TConverterThread.Start;
begin

end;

end.